package com.ctakit.kiwi.common.core.bean.selectors;

import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;

/**
 * A selector that sorted some of the result key-value pairs into a map which has the same prefix ended with ".".
 * For example,
 * <ul>
 * <li>Without this: result of select("a1, a2.b, a2.c") is a list of map with 3 keys: a1, a2.b, a2.c.</li>
 * <li>With this, result of select("a1, a2.b, a2.c") will be a list of map with 2 keys: a1, a2. and the value of a2 is a map with keys: b and c</li>
 * </ul>
 * <p>
 * This class is created to fulfill requirement: https://github.com/Jimmy-Shi/bean-query/issues/9
 *
 * @author lizhenmin
 */
public class NestedKeyValueMapSelector extends KeyValueMapSelector {
    private KeyValueMapSelector selector;

    public NestedKeyValueMapSelector(KeyValueMapSelector sourceSelector) {
        this.selector = sourceSelector;
    }

    @Override
    protected Map<String, Object> doSelect(Object item) {
        Map<String, Object> originalResult = this.selector.select(item);
        if (MapUtils.isEmpty(originalResult)) {
            return originalResult;
        }

        Map<String, Object> result = new LinkedHashMap<String, Object>();
        for (Entry<String, Object> originalItem : originalResult.entrySet()) {
            String key = originalItem.getKey();
            Object value = originalItem.getValue();

            if (StringUtils.isBlank(key) || (!key.contains("."))) {
                result.put(key, value);
                continue;
            }
            addNestedKeyValue(key, value, result);
        }

        return result;
    }

    private void addNestedKeyValue(String key, Object value, Map<String, Object> container) {
        if (!key.contains(".")) {
            container.put(key, value);
            return;
        }

        String firstKey = StringUtils.substringBefore(key, ".");
        Map<String, Object> subContainer = new LinkedHashMap<String, Object>();
        if (container.containsKey(firstKey)) {
            Object originalValue = container.get(firstKey);
            if (subContainer.getClass().isAssignableFrom(originalValue.getClass())) {
                subContainer = (Map<String, Object>) originalValue;
            } else {
                subContainer.put("", originalValue);
                container.put(firstKey, subContainer);
            }
        } else {
            container.put(firstKey, subContainer);
        }
        addNestedKeyValue(StringUtils.substringAfter(key, "."), value, subContainer);
    }

}
